﻿@using Flagrum.Web.Persistence.Entities
@using Flagrum.Web.Features.AssetExplorer.Base
@using Flagrum.Web.Features.AssetExplorer.Data
@using Flagrum.Core.Gfxbin.Gmtl.Data
@using Flagrum.Core.Utilities
@using Flagrum.Core.Gfxbin.Data
@using Flagrum.Core.Gfxbin.Gmtl
@using System.IO
@using System.Text
@using Flagrum.Core.Archive
@using Flagrum.Core.Gfxbin.Btex
@using BinaryReader = Flagrum.Core.Gfxbin.Serialization.BinaryReader
@inject IWpfService WpfService
@inject FlagrumDbContext Context
@inject IStringLocalizer<ExportWithDependenciesModal> L
@inject AppStateService AppState
@inject ProfileService Profile

<Modal @ref="Modal">
    <HeaderView>
        <span class="text-grey-300 flex-grow">@L["ExportWithDependencies"]</span>
        <span class="material-icons cursor-pointer" @onclick="Close">cancel</span>
    </HeaderView>
    <BodyView>
        <div class="h-full">
            <div class="row">
                <label style="width: 250px">@L["TextureFormat"]</label>
                <select @bind="Extension" class="input bg-dark-550 w-full">
                    <option>PNG</option>
                    <option>TGA</option>
                    <option>DDS</option>
                    <option>BTEX</option>
                </select>
            </div>
            <div class="row mt-6">
                <Checkbox IsChecked="@IncludeHighTextures" OnChange="v => { IncludeHighTextures = v; StateHasChanged(); }"/>
                <span class="inline-block ml-2">@L["IncludeHighTextures"]</span>
            </div>
            <div class="row mt-6 mb-10">
                <Checkbox IsChecked="@IncludeCommonTextures" OnChange="v => { IncludeCommonTextures = v; StateHasChanged(); }"/>
                <span class="inline-block ml-2">@L["IncludeSharedTextures"]</span>
            </div>
            <Button Text="@L["Export"]" Icon="file_download" OnClickAsync="Export"/>
        </div>
    </BodyView>
</Modal>

@code
{
    [CascadingParameter(Name = "AssetExplorer")]
    public AssetExplorer Parent { get; set; }

    private Modal Modal { get; set; }
    private string Extension { get; set; } = "PNG";
    private bool IncludeHighTextures { get; set; } = true;
    private bool IncludeCommonTextures { get; set; }
    private IAssetExplorerNode ContextNode { get; set; }

    public void Open(IAssetExplorerNode contextNode)
    {
        ContextNode = contextNode;
        AppState.IsModalOpen = true;
        WpfService.Set3DViewportVisibility(false);
        Modal.Open();
    }

    private void Close()
    {
        Modal.Close();
        AppState.IsModalOpen = false;

        if (AppState.Is3DViewerOpen)
        {
            WpfService.Set3DViewportVisibility(true);
        }
    }

    private async Task Export()
    {
        var item = ContextNode;
        var defaultName = item.Name + ".gfxbin";
        const string filter = "Game Model|*.gmdl.gfxbin";

        await WpfService.OpenSaveFileDialogAsync(defaultName, filter, path =>
        {
            InvokeAsync(() =>
            {
                Parent.SetLoading(true, "Exporting");
                Close();
            });

    // For some reason this ends up with multiple copies of the extension for some people
    // This will ensure all copies are wiped, and then we'll just add ONE set back
            path = path.Replace(".gmdl.gfxbin", "");
            path += ".gmdl.gfxbin";

            var gfxbin = item.Data;
            var reader = new BinaryReader(gfxbin);
            var header = new GfxbinHeader();
            header.Read(reader);

            var materials = new Dictionary<string, byte[]>();
            var highMaterials = new Dictionary<string, byte[]>();
            var textures = new Dictionary<string, byte[]>();
            var highTextures = new Dictionary<string, byte[]>();
            
            var root = Path.GetDirectoryName(path);
            var materialsDirectory = $"{root}\\materials";
            var texturesDirectory = $"{root}\\sourceimages";
            var highImagesDirectory = $"{root}\\highimages";

            var materialUris = header.Dependencies
                .Where(d => d.Path.EndsWith(".gmtl"))
                .Select(d => d.Path);

            foreach (var uri in materialUris)
            {
                var materialData = Context.GetFileByUri(uri);
                materials.Add(uri, materialData);

                var material = new MaterialReader(materialData).Read();
                foreach (var texture in material.Textures.Where(t => !string.IsNullOrEmpty(t.Path) && !t.Path.EndsWith(".sb")))
                {
                    if (textures.ContainsKey(texture.Path) || !CommonCheck(texture.Path))
                    {
                        continue;
                    }

                    var textureData = Context.GetFileByUri(texture.Path);

                    if (textureData.Length > 0)
                    {
                        ConvertTexture(texture.Path, texturesDirectory, textureData);
                    }
                }

                if (IncludeHighTextures && !string.IsNullOrWhiteSpace(material.HighTexturePackAsset))
                {
    // Process high resolution textures
                    var highTexturePackUri = material.HighTexturePackAsset.Replace(".htpk", ".autoext");
                    var highMaterial = ProcessHighTextures(material, highTexturePackUri, texturesDirectory, textures, false);
                    if (highMaterial.Length > 0)
                    {
                        highMaterials.Add(highTexturePackUri, highMaterial);
                    }

    // Attempt to process 4K texture pack
                    highTexturePackUri = highTexturePackUri.Insert(highTexturePackUri.LastIndexOf('.'), "2");
                    highMaterial = ProcessHighTextures(material, highTexturePackUri, highImagesDirectory, highTextures, true);
                    if (highMaterial.Length > 0)
                    {
                        highMaterials.Add(highTexturePackUri, highMaterial);
                    }
                }
            }

            File.WriteAllBytes(path, gfxbin);

            var gpubinUris = header.Dependencies
                .Where(d => d.Path.EndsWith(".gpubin"))
                .Select(d => d.Path);

            foreach (var gpubinUri in gpubinUris)
            {
                var gpubinData = Context.GetFileByUri(gpubinUri);
                if (gpubinData.Length > 0)
                {
                    var gpubinPath = $@"{root}\{gpubinUri.Split('/').Last()}";
                    File.WriteAllBytes(gpubinPath, gpubinData);
                }
            }

            foreach (var (uri, data) in materials)
            {
                var materialPath = $"{materialsDirectory}\\{uri.Split('/').Last()}.gfxbin";
                IOHelper.EnsureDirectoriesExistForFilePath(materialPath);
                File.WriteAllBytes(materialPath, data);
            }

            foreach (var (uri, data) in highMaterials)
            {
                var materialPath = $"{materialsDirectory}\\{uri.Split('/').Last()}";
                IOHelper.EnsureDirectoriesExistForFilePath(materialPath);
                File.WriteAllBytes(materialPath, data);
            }

            InvokeAsync(() => Parent.SetLoading(false));
        });
    }

    private byte[] ProcessHighTextures(Material material, string highTexturePackUri, string directory, Dictionary<string, byte[]> textures, bool is4K)
    {
        byte[] highTexturePack = null;
        if (Context.ArchiveExistsForUri(highTexturePackUri))
        {
            highTexturePack = Context.GetFileByUri(highTexturePackUri);
        }
        else if (!is4K)
        {
            var archiveRelativePath = material.HighTexturePackAsset
                .Replace("data://", "")
                .Replace(".htpk", ".earc")
                .Replace('/', '\\');

            var archivePath = $"{Profile.GameDataDirectory}\\{archiveRelativePath}";

            if (File.Exists(archivePath))
            {
                using var unpacker = new EbonyArchive(archivePath);
                var files = string.Join(' ', unpacker.Files.Select(f => f.Value.Uri));
                highTexturePack = Encoding.UTF8.GetBytes(files);
            }
        }

        if (highTexturePack != null)
        {
            foreach (var highTexture in Encoding.UTF8.GetString(highTexturePack)
                .Split(' ')
                .Where(s => !string.IsNullOrWhiteSpace(s))
                .Select(s =>
                {
                    var result = s.Trim();
                    if (result.Last() == 0x00)
                    {
                        result = result[..^1];
                    }

                    return result;
                }))
            {
                if (textures.ContainsKey(highTexture) || !CommonCheck(highTexture))
                {
                    continue;
                }

                var textureData = Context.GetFileByUri(highTexture);

                if (textureData.Length > 0)
                {
                    ConvertTexture(highTexture, directory, textureData);
                    textures.Add(highTexture, null);
                }
            }

            return highTexturePack;
        }

        return Array.Empty<byte>();
    }

    private bool CommonCheck(string uri)
    {
        return IncludeCommonTextures
               || (!uri.StartsWith("data://shader")
                   && !uri.StartsWith("data://vfx")
                   && !uri.Contains("/common/"));
    }

    private void ConvertTexture(string uri, string directory, byte[] btex)
    {
        var fileName = uri.Split('/').Last();
        fileName = fileName[..fileName.LastIndexOf('.')] + "." + Extension.ToLower();
        var texturePath = $"{directory}\\{fileName}";
        
        new TextureConverter(Profile.Current.Type).ExportTexture(texturePath, (ImageFormat)Extension, btex);
    }
}